/**
 * Configuration
 * -------------------------------------------------
 * 
 * This class will creates configurations with automata.
 * 
 * @author abrao
 */
package model.constraints.inherited;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Hashtable;

import model.constraints.AbstractConstraint;
import model.constraints.synthesized.SyntAttributesXDF;
import automata.Fsa;
import automata.State;
import automata.StateSet;

public class Configuration implements Comparable<Configuration> {
	
	private State s;
	private Fsa f;
	private AbstractConstraint k;

	public Configuration(State s, Fsa f, AbstractConstraint k) {
		this.s = s;
		this.f = f;
		this.k = k;
	}

	public Configuration() {}
		
	public State getState() {
		return s;
	}

	public Fsa getFsa() {
		return f;
	}

	public AbstractConstraint getKey() {
		return k;
	}	
	
	/**
	 * Checks that arrived at the end of automaton Source 
	 * @return true or false 
	 */
	public boolean isSourceEnd(){
		
		Fsa f = getFsa();
		State sf = getState();
		StateSet setF = f.get_final_states();
		AbstractConstraint k = this.getKey();
		
		int fposition = k.getFsaPosition(f);
		
		if ((fposition == 2) && (setF.contains(sf)))
			return true;
		
		return false;
	}
	
	/**
	 * Checks that arrived at the end of automaton Target 
	 * @return true or false 
	 */
	public boolean isTargetEnd(){
		Fsa f = getFsa();
		State sf = getState();
		StateSet setF = f.get_final_states();
		AbstractConstraint k = this.getKey();
		int fposition = k.getFsaPosition(f);
		
		if ((fposition == 3) && (setF.contains(sf)))
			return true;
		
		return false;
	}
	
	public int getPosition(){
		return k.getJ(s.elt);
	}
	
	public String getEquality(){
		return k.getEquality(f, s);
	}
	
	public SyntAttributesXDF validateData(SyntAttributesXDF syntAtt, String sb, String posAux, String nodeName,
			                           Hashtable<String,ArrayList<Inters>> htIntersXfd){
		SyntAttributesXDF syntAttAux = new SyntAttributesXDF();
		int dsj, npaths;
		
		if (isSourceEnd()) { //M =  T' ^ e = Ft' 
			//System.out.println("Fim do Source End");			
			syntAtt.setNodeName(nodeName);
			npaths= getKey().getNPaths();
			syntAtt.setDsSize(npaths);
			dsj = getPosition();
			String igualdade = getEquality();
			
			if(igualdade.equalsIgnoreCase("V"))
				syntAtt.setDs(sb,dsj);
			else
				syntAtt.setDs(posAux,dsj);
			
			for (int i=0;i<npaths;i++) {
				if ((i!=dsj)&& syntAtt.getDs().get(i).equals(""))
				    syntAtt.setDs("#",i);
			}				
		} else {
			//System.out.println("nao eh fim do node");
			syntAtt.setNodeName(nodeName);
			npaths= getKey().getNPaths();
			syntAtt.setDsSize(npaths);
			
			for (int i=0;i<npaths;i++) {
				if (syntAtt.getDs().get(i).equals(""))
					syntAtt.setDs("#",i);					 
			}			
		}	   
		
		if (isTargetEnd()){ // M =  T" ^ e = Ft"
		   syntAtt.setNodeName(nodeName);
		   String igualdade = getEquality();
		   
		   if(igualdade.equalsIgnoreCase("V"))
			   syntAtt.setDc(sb);
		   else
			   syntAtt.setDc(posAux);		   	  
		} else {
			if (syntAtt.getDc().equals(""))
				syntAtt.setDc("#");
		} 
		
		Inters intersection1 = new Inters();
		intersection1.setDc(syntAtt.getDc());
	   	intersection1.setDs(syntAtt.getDs());
	    
	   	if (syntAtt.getInters().isEmpty())
	    	syntAtt.addInters(intersection1);
	    else {
	    	syntAttAux.addInters(intersection1);
	        syntAtt.join(syntAttAux,htIntersXfd,posAux);
	    }    
	    
	   	return syntAtt;
	}
	
	public SyntAttributesXDF validateInNode(SyntAttributesXDF syntAtt, SyntAttributesXDF syntAttAnt, 
			                             String posAnt, String posAt, String nodeName, Hashtable<String, Hashtable<ArrayList<String>, String>> htContextXdf, 
			                             Hashtable<String,ArrayList<Inters>> htIntersXfd, Hashtable<String, ArrayList<String>> hterror) {						
		
		Hashtable<ArrayList<String>, String> htXdf = new Hashtable<ArrayList<String>, String>();
		
		int role = roleType(this);
		int dsj;
		/*
		System.out.println("-- Role :"+role);
		System.out.println("-- Node Name :"+nodeName.toString());
		System.out.println("-- SyntAtt "+syntAtt.toString());
		System.out.println("-- SyntAttAnt "+syntAttAnt.toString());
		*/
		switch(role) {
			case 1: // Estado final para alguma dependencia, num no que nao eh folha, lado direito (eh isso?)
				syntAtt = validateData(syntAtt, syntAtt.getDc(), posAt, nodeName, htIntersXfd);
				syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
				break;
			
			case 2: // in function validateData
				syntAtt.setNodeName(nodeName);
				dsj = getPosition();
				syntAtt = validateData(syntAtt,(String)syntAtt.getDs().get(dsj), posAt, nodeName, htIntersXfd);
				syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
				break;
			
			case 3: // automata right side	
			     if (!syntAtt.getDc().equals("") && !syntAtt.getDs().isEmpty()) { 
				    syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
				    syntAtt.setDc("");
				    syntAtt.setDs(new ArrayList<String>());
			     } else 
				    syntAttAnt.join(syntAtt,htIntersXfd,posAnt); 
			     
				break;
			
			case 4: // automata left side		
			    if (!syntAtt.getDc().equals("") && !syntAtt.getDs().isEmpty()) { 			   
			      syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
			      syntAtt.setDc("");
			      syntAtt.setDs(new ArrayList<String>());
			    } else
				    syntAttAnt.join(syntAtt,htIntersXfd,posAnt); 			     		
				
				break;
				
			case 5: // Context Automaton - Final State
				Boolean erro = false;
				ArrayList<Inters> al =  syntAtt.getInters();
									
				for(Iterator<Inters> j=al.iterator(); j.hasNext();) {
					Inters elInters = (Inters) j.next();
					
					if (!htXdf.containsKey(elInters.getDs()))
						htXdf.put(elInters.getDs(), elInters.getDc());
					else {
						String s = (String) htXdf.get(elInters.getDs());
					 		
						if (!s.equals(elInters.getDc())) {
							erro = true;		
							//insert error in hash
							ArrayList<String> array = hterror.get(k.getNameConstraint());
							String dc = htXdf.get(elInters.getDs());
							array.add(elInters.getDs().toString() + " : " + elInters.getDc().toString() + " HAS KEY " + elInters.getDs().toString() + " : " + dc );					 			
					 		hterror.put(k.getNameConstraint(), array);					 	
					 	}    
					}	    
		       }
				
			   if (erro == false) 
			      syntAttAnt.addC(true);
			   else
				  syntAttAnt.addC(false);
			   
			   htContextXdf.put(posAt, htXdf);	
			   
			   break;
			   
			case 6:
				Boolean element;
				Boolean ver=true;
				
				ArrayList<Boolean> al2 = (ArrayList<Boolean>) syntAttAnt.getC();
				if (!al2.isEmpty()) {
					for(Iterator<Boolean> j=al2.iterator(); j.hasNext();) {
						element = (Boolean) j.next();
						ver = element && ver;
					}
				} else 
					ver = true; 				
				
				ArrayList<Boolean> al1 = (ArrayList<Boolean>) syntAtt.getC();
				for(Iterator<Boolean> j=al1.iterator(); j.hasNext();) {
					element = (Boolean) j.next();
					ver = element && ver;
				}
				
				ArrayList<Boolean> al3 = new ArrayList<Boolean>();
				al3.add(ver);
				syntAttAnt.setC(al3); 					
				break;
				
			default:
				//working...
				break;
		}

		return syntAttAnt;
	}
	
	/**
	 * This rule defines which follow from the verification of the final state and current state
	 * @param Configuration c (object)
	 * @return int
	 */
	public int roleType(Configuration c) {
		
		Fsa f = c.getFsa();
		State sf = c.getState();
		StateSet setF = f.get_final_states();
		AbstractConstraint k = (AbstractConstraint) c.getKey();
		int fposition = k.getFsaPosition(f);
		
		if (fposition == 3){ //T" - right side
			if (setF.contains(sf))
				return 1; 
			else
				return 3;
		}
		
		if (fposition == 2){ //T' - left side	
			if (setF.contains(sf))
				return 2;
			else
				return 4;
		}
		
		if (fposition == 1){ //M - context
			if (setF.contains(sf))
				return 5;
			else
				return 6;
		}
		
		return 0;
	}

	/**
	 * This function will sort the automata (context, left, right)
	 * - this class should implements "Comparable"
	 * @param Configuration that will compare
	 * @return int lower or higher
	 */
	@Override
	public int compareTo(Configuration conf) {		
		int thisPosition = ((AbstractConstraint) this.getKey()).getFsaPosition(this.getFsa());
		int objPosition = ((AbstractConstraint) conf.getKey()).getFsaPosition(conf.getFsa());
				
		return thisPosition < objPosition ? + 1 : (thisPosition > objPosition ? -1 : 0); 
	}
	
	public String toString() {
		String str="";
		int fsaType;
		fsaType = ((AbstractConstraint) getKey()).getFsaPosition(getFsa());
		
		switch(fsaType){
			case 1: str += "M.";
					break;
			case 2: str += "T'.";
					break;
			case 3: str += "T\".";
					break;
		}
		
		str += "e"+(getState().elt);
		
		return str;
	}	
}